﻿/*--------------------------------------------------------------
 * Author : Shine K Velayudhan
 ---------------------------------------------------------------*/

using System;
using System.Collections.Generic;
using System.IO;

namespace IQN.Cache
{
    /// <summary>
    /// This class holds bucket in which serialized objects (BucketEntry) are cached.
    /// </summary>
    internal sealed class HashBucket
    {
        /// <summary>
        /// BucketEntry list.
        /// </summary>
        private readonly List<BucketEntry> _bucketEntries = new List<BucketEntry>(10);

        /// <summary>
        /// Add method
        /// </summary>
        /// <param name="entry">Bucket Entry</param>
        public void AddItem(BucketEntry entry)
        {
            this._bucketEntries.Add(entry);
        }

        /// <summary>
        /// Load all BucketEntries and return the whole bucket.
        /// </summary>
        /// <param name="fileName">File name</param>
        /// <returns>Bucket</returns>
        public static HashBucket Load(string fileName)
        {
            if (null == fileName)
            {
                throw new ArgumentNullException("fileName");
            }
            if (!File.Exists(fileName))
            {
                throw new FileNotFoundException("File not found: " + fileName);
            }
            HashBucket bucket = new HashBucket();
            using (FileStream stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                using (BinaryReader reader = new BinaryReader(stream))
                {
                    int num = reader.ReadInt32();
                    for (int i = 0; i < num; i++)
                    {
                        BucketEntry item = BucketEntry.Deserialize(reader);
                        bucket._bucketEntries.Add(item);
                    }
                }
            }
            return bucket;
        }

        /// <summary>
        /// Remove cached item.
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="id">id</param>
        public void RemoveItem(string key, string id)
        {
            BucketEntry item = null;
            if (this.TryGetItem(key, out item, id))
            {
                this._bucketEntries.Remove(item);
            }
        }

        /// <summary>
        /// Save Bucket to file system.
        /// </summary>
        /// <param name="fileName">file name</param>
        public void Save(string fileName)
        {
            if (null == fileName)
            {
                throw new ArgumentNullException("fileName");
            }
            using (FileStream stream = File.Create(fileName))
            {
                using (BinaryWriter writer = new BinaryWriter(stream))
                {
                    writer.Write(this._bucketEntries.Count);
                    foreach (BucketEntry entry in this._bucketEntries)
                    {
                        entry.Serialize(writer);
                    }
                }
            }
        }

        /// <summary>
        /// Try to retrieve BucketEntry from cached bucket.
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="item">item</param>
        /// <param name="id">id</param>
        /// <returns>True if item available.</returns>
        public bool TryGetItem(string key, out BucketEntry item, string id)
        {
            foreach (BucketEntry entry in this._bucketEntries)
            {
                if ((null != entry) && (entry.Key == key) && entry.Id == id)
                {
                    item = entry;
                    return true;
                }
            }
            item = null;
            return false;
        }

        /// <summary>
        /// Total count of item in cache.
        /// </summary>
        public int ItemCount
        {
            get
            {
                return this._bucketEntries.Count;
            }
        }
    }
}